package svgedit.gui.actions;

import java.awt.Cursor;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;

import javax.swing.event.MouseInputListener;

import svgedit.gui.Marker;
import svgedit.gui.Toolbar;
import svgedit.gui.View;
import svgedit.svg.SVGCircleElement;
import svgedit.svg.SVGElement;
import svgedit.svg.SVGLength;
import svgedit.svg.SVGLineElement;
import svgedit.svg.SVGRectElement;
import svgedit.svg.SVGStylable;

/**
 * This listener handles the mouse actions that occur on the viewport It handles
 * dragging shapes, resizing them and selecting them This class is also
 * responsible for parsing the proper calculated coordinates the the view to
 * correctly render the selection box
 * 
 * @author SARJ
 * 
 */
public class ViewListener implements MouseInputListener, MouseMotionListener
{

	private View view;
	private Toolbar toolbar;

	// Initial co-ordinates of mouse (mousePressed)
	private int initX;
	private int initY;

	// Current co-ordinates of mouse when dragging (mouseDragged)
	private int curX;
	private int curY;

	// The selected marker
	private Marker selectedMarker;

	// THe new element that will be drawn
	private SVGElement elementDraw;

	// The rectangle that is drawn when the mouse is dragged
	private Rectangle selectRect;

	// The shape about to be drawn, if any
	private ShapeType shapeType = ShapeType.NONE;

	/**
	 * Returns the selection rect
	 * 
	 * @return Returns a rectangle of the selection rect bounds
	 */
	public Rectangle getSelectRect()
	{
		return selectRect;
	}

	/**
	 * Constructs the ViewListener
	 * 
	 * @param view
	 *            Reference to view panel
	 * @param toolbar
	 *            Reference to toolbar
	 */
	public ViewListener(View view, Toolbar toolbar)
	{
		this.toolbar = toolbar;
		this.view = view;
		// Null pointer avoiding
		this.selectRect = new Rectangle(0, 0, 0, 0);
	}

	@Override
	public void mousePressed(MouseEvent e)
	{

		// Set coords of mouse
		selectRect.x = e.getX();
		selectRect.y = e.getY();
		selectRect.width = 1;
		selectRect.height = 1;

		// Used for inverse selection (bottom-right to top-left)
		initX = e.getX();
		initY = e.getY();

		// Loop through all selectedMarkers
		for (Marker marker : view.getMarkers())
		{
			// Did mouse intersect marker
			if (marker.getBounds().contains(selectRect))
			{
				selectedMarker = marker;
				return;
			}
		}

		// Check the current shape that is trying to be drawn, if any
		switch (getShapeType())
		{

			// Break out of method here
			case NONE:
				view.setSelectRect(selectRect, false);
				view.repaint();
				return; // do not continue down unless adding a shape

			case CIRCLE:
				// Get circle properties
				elementDraw = new SVGCircleElement(view.getDocument());

				((SVGCircleElement) elementDraw).getCX().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, e.getX()));
				((SVGCircleElement) elementDraw).getCY().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, e.getY()));

				// These lines below stop it from throwing exceptions
				// because it doen't like being parsed an incomplete
				// shape - 0 lengths
				((SVGCircleElement) elementDraw).getR().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, 1));

				break;

			case RECT:

				// Get rectangle properties
				elementDraw = new SVGRectElement(view.getDocument());

				((SVGRectElement) elementDraw).getX().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, e.getX()));
				((SVGRectElement) elementDraw).getY().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, e.getY()));

				// These lines below stop it from throwing exceptions
				// because it doen't like being parsed an incomplete
				// shape - 0 lengths
				((SVGRectElement) elementDraw).getWidth().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, 1));

				((SVGRectElement) elementDraw).getHeight().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, 1));

				break;

			case LINE:

				// Get line properties
				elementDraw = new SVGLineElement(view.getDocument());

				((SVGLineElement) elementDraw).getX1().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, e.getX()));
				((SVGLineElement) elementDraw).getY1().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, e.getY()));

				// These two lines stop it from throwing exceptions
				// because it doen't like being parsed an incomplete
				// shape - 0 lengths
				((SVGLineElement) elementDraw).getX2()
						.setValueFromLength(
								new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, e
										.getX() + 1));

				((SVGLineElement) elementDraw).getY2()
						.setValueFromLength(
								new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, e
										.getY() + 1));

				break;

			// Can't draw groups so return aswell
			case GROUP:
				break;

		}

		// Style the element
		SVGStylable styleDraw = (SVGStylable) elementDraw;
		styleDraw.getFill().setValueFromPaint(((toolbar.getFillColor())));
		styleDraw.getStroke().setValueFromPaint(((toolbar.getStrokeColor())));

		styleDraw.getStrokeWidth().setValueFromString(toolbar.getStrokeWidth());

		// Add element to rootgroup
		view.getDocument().getRootGroup().appendChild(elementDraw);

		view.repaint();

	}

	@Override
	public void mouseDragged(MouseEvent e)
	{
		// Store the current mouse pos
		curX = e.getX();
		curY = e.getY();

		// Variables for drawing the selection rect
		int width = curX - initX;
		int height = curY - initY;

		// Draw/Create selectRect

		// If drawing a reverse selection
		if (width < 0)
		{
			width = Math.abs(curX - initX);
			selectRect.x = curX;
			selectRect.width = width;
		}
		else
			selectRect.width = Math.abs(e.getX() - selectRect.x);

		// If drawing a reverse selection
		if (height < 0)
		{
			height = Math.abs(e.getY() - initY);
			selectRect.y = curY;
			selectRect.height = height;
		}
		else
			selectRect.height = Math.abs(e.getY() - selectRect.y);

		// Handle makers

		// there is a marker selected
		if (selectedMarker != null)
		{
			// Drag the selected marker
			selectedMarker.drag(initX - curX, initY - curY);
			initX = curX;
			initY = curY;

			view.setSelectRect(null, false);
			view.repaint();

			return;
		}

		// Draw shape
		else
		// Draw shape if a tool is selected
		if (getShapeType() != ShapeType.NONE)
		{
			drawShape(width, height);
			return;
		}
		else
			view.setSelectRect(selectRect, true);

		view.repaint();

	}

	/**
	 * Draw a new shape and make its bounds as specified
	 * 
	 * @param width
	 *            The width of the element
	 * @param height
	 *            The height of the element
	 */
	private void drawShape(int width, int height)
	{
		// Shape to type to be drawn
		switch (getShapeType())
		{
			case NONE:
				// Start drawing rect
				view.setSelectRect(selectRect, true);
				view.repaint();

				break;

			case CIRCLE:
				// Stop drawing rect
				view.setSelectRect(null, false);

				((SVGCircleElement) elementDraw).getR().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, Math.max(
								width, height)));

				break;

			case RECT:
				// Stop drawing rect
				view.setSelectRect(null, false);

				// Just copy the same coordinates as selectRect
				((SVGRectElement) elementDraw).getX()
						.setValueFromLength(
								new SVGLength(SVGLength.SVG_LENGTHTYPE_PX,
										selectRect.x));

				((SVGRectElement) elementDraw).getY()
						.setValueFromLength(
								new SVGLength(SVGLength.SVG_LENGTHTYPE_PX,
										selectRect.y));

				((SVGRectElement) elementDraw).getWidth().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX,
								selectRect.width));

				((SVGRectElement) elementDraw).getHeight().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX,
								selectRect.height));

				break;

			case LINE:
				// Stop drawing rect
				view.setSelectRect(null, false);

				// SVGLineElement handles negative coordinates already;
				((SVGLineElement) elementDraw).getX2().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, curX));

				((SVGLineElement) elementDraw).getY2().setValueFromLength(
						new SVGLength(SVGLength.SVG_LENGTHTYPE_PX, curY));
				break;
		}
		view.repaint();
	}

	@Override
	public void mouseReleased(MouseEvent e)
	{

		// Parse the selectRect to the view panel, but DO NOT Paint it,
		// This is for when the user just clicks on something to select it,
		// (as opposed to dragging)
		view.setSelectRect(selectRect, false);
		view.repaint();

		// Stop dragging marker
		selectedMarker = null;

		// Stop drawing any shape
		setShapeType(ShapeType.NONE);

		// Change mouse cursor
		Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
		view.setCursor(defaultCursor);

	}

	/**
	 * Attempt to find the shape that intersects the mouse selection and then
	 * draw the appropriate border around it
	 */
	@Override
	public void mouseClicked(MouseEvent e)
	{

		// Set the selectRect bounds
		selectRect.x = e.getX();
		selectRect.y = e.getY();
		selectRect.width = 1;
		selectRect.height = 1;

		// Parse the selectRect to the view panel, but DO NOT Paint it,
		// This is for when the user just clicks on something to select it,
		// (as opposed to dragging)
		view.setSelectRect(selectRect, false);
		view.repaint();

	}

	/**
	 * Get the shape that is about to be drawn
	 * 
	 * @return the shapeType an enum of the shape type to draw
	 */
	public ShapeType getShapeType()
	{
		return shapeType;
	}

	/**
	 * Sets the shape that is about to be drawn
	 * 
	 * @param shapeType
	 *            the shapeType to set
	 */
	public void setShapeType(ShapeType shapeType)
	{
		this.shapeType = shapeType;
	}

	/**
	 * Used to determine what shape we are "drawing"
	 */
	public enum ShapeType
	{

		/** Don't attempt to draw anything */
		NONE,

		/** When ready to draw a rect */
		RECT,

		/** When ready to draw a circle */
		CIRCLE,

		/** When ready to draw a line */
		LINE,

		/** For selecting a group */
		GROUP, UNGROUP

	}

	@Override
	public void mouseExited(MouseEvent e)
	{
	}

	@Override
	public void mouseEntered(MouseEvent e)
	{
	}

	@Override
	public void mouseMoved(MouseEvent e)
	{
	}

}
